﻿
using Common;
/*
/// 
/// Portions Copyright 1999-2004 Carnegie Mellon University.  
/// Portions Copyright 2004 Sun Microsystems, Inc.  
/// Portions Copyright 2004 Mitsubishi Electric Research Laboratories.
/// All Rights Reserved.  Use is subject to license terms.
/// 
/// See the file "license.terms" for information on usage and
/// redistribution of this file, and for a DISCLAIMER OF ALL 
/// WARRANTIES.
 *
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Linguist.Acoustic
{
    /// <summary>
    /// Represents a unit of speech. Units may represent phones, words or any other suitable unit
    /// </summary>
    public class Unit:IUnit
    {
        public static Unit[] EMPTY_ARRAY = new Unit[0];

        private String name = String.Empty;
        private Boolean filler=false;
        private Boolean silence=false;
        private int baseID=-1;
        private IUnit baseUnit=null;
        private IContext context=null;

        private volatile String key = String.Empty;

        /// <summary>
        /// Constructs a context independent unit. Constructors are package private, use the UnitManager to create and access
        /// units.
        /// </summary>
        /// <param name="name">the name of the unit</param>
        /// <param name="filler"><code>true</code> if the unit is a filler unit</param>
        /// <param name="id">the base id for the unit</param>
        public Unit(String name, Boolean filler, int id)
        {
            this.name = name;
            this.filler = filler;
            this.silence = name.Equals(UnitManager.SILENCE_NAME);
            this.baseID = id;
            this.baseUnit = this;
            this.context = Context.EMPTY_CONTEXT;
        }
        /// <summary>
        /// Constructs a context independent unit. Constructors are package private, use the UnitManager to create and access
        /// units.
        /// </summary>
        /// <param name="baseUnit">the base id for the unit</param>
        /// <param name="filler"><code>true</code> if the unit is a filler unit</param>
        /// <param name="id">the base id for the unit</param>
        public Unit(IUnit baseUnit, Boolean filler, IContext context)
        {
            this.name = baseUnit.getName();
            this.filler = filler;
            this.silence = name.Equals(UnitManager.SILENCE_NAME);
            this.baseID = baseUnit.getBaseID();
            this.baseUnit = baseUnit;
            this.context = context;
        }
        /// <summary>
        /// Gets the name for this unit
        /// </summary>
        /// <returns>the name for this unit</returns>
        public String getName()
        {
            return name;
        }
        /// <summary>
        /// Determines if this unit is a filler unit
        /// </summary>
        /// <returns></returns>
        public Boolean isFiller()
        {
            return filler;
        }
        /// <summary>
        /// Determines if this unit is the silence unit
        /// </summary>
        /// <returns></returns>
        public Boolean isSilence()
        {
            return silence;
        }
        /// <summary>
        /// Gets the  base unit id associated with this HMM
        /// </summary>
        /// <returns></returns>
        public int getBaseID()
        {
            return baseID;
        }

        /// <summary>
        /// Gets the  base unit associated with this HMM
        /// </summary>
        /// <returns></returns>
        public IUnit getBaseUnit()
        {
            return baseUnit;
        }

        /// <summary>
        /// Returns the context for this unit
        /// </summary>
        /// <returns></returns>
        public IContext getContext() 
        {
            return context;
        }
        /// <summary>
        /// Determines if this unit is context dependent
        /// </summary>
        /// <returns></returns>
        public Boolean isContextDependent() {
            return getContext() != Context.EMPTY_CONTEXT;
        }
        /// <summary>
        /// gets the key for this unit
        /// </summary>
        /// <returns></returns>
        public String getKey() 
        {
            return ToString();
        }

        /// <summary>
        /// Checks to see of an object is equal to this unit
        /// </summary>
        /// <param name="o">the object to check</param>
        /// <returns>true if the objects are equal</returns>
        public Boolean Equals(Object o) 
        {
            if (this == o) 
            {
                return true;
            } 
            else if (o is Unit) 
            {
                Unit otherUnit = (Unit) o;
                return getKey().Equals(otherUnit.getKey());
            } 
            else 
            {
                return false;
            }
        }
        /// <summary>
        /// calculates a hashCode for this unit. Since we defined an equals for Unit, we must define a hashCode as well
        /// </summary>
        /// <returns>the hashcode for this object</returns>
        public int hashCode() 
        {
            return getKey().GetHashCode();
        }
        /// <summary>
        /// Converts to a string
        /// </summary>
        /// <returns>string version</returns>
        public String ToString() 
        {
            if (key != null) 
            {
                if (context == Context.EMPTY_CONTEXT) 
                {
                    key = (filler ? "*" : "") + name;
                } 
                else {
                    key = (filler ? "*" : "") + name + '[' + context + ']';
                }
            }
            return key;
        }
        /// <summary>
        /// Checks to see if the given unit with associated contexts is a partial match for this unit.   Zero, One or both
        /// contexts can be null. A null context matches any context
        /// </summary>
        /// <param name="name">the name of the unit</param>
        /// <param name="context">the  context to match against</param>
        /// <returns>true if this unit matches the name and non-null context</returns>
        public Boolean isPartialMatch(String name, IContext context) 
        {    
            return getName().Equals(name) && context.isPartialMatch(this.context);
        }

        /// <summary>
        /// Creates and returns an empty context with the given size. The context is padded with SIL filler
        /// </summary>
        /// <param name="size">the size of the context</param>
        /// <returns>the context</returns>
        public static IUnit[] getEmptyContext(int size) 
        {
            Unit[] context = new Unit[size];
            Array.ForEach(context,new Action<Unit>(UnitInit));
            return context;
        }

        private static void UnitInit(IUnit val)
        {
            val = UnitManager.SILENCE;
        }
        /// <summary>
        /// Checks to see that there is 100% overlap in the given contexts
        /// </summary>
        /// <param name="a">context to check for a match</param>
        /// <param name="b">context to check for a match</param>
        /// <returns><code>true</code> if the contexts match</returns>
        public static Boolean isContextMatch(IUnit[] a, IUnit[] b) 
        {
            if (a == null || b == null) 
            {
                return a == b;
            } 
            else if (a.Length != b.Length) 
            {
                return false;
            } 
            else 
            {
                for (int i = 0; i < a.Length; i++) 
                {
                    if (!a[i].getName().Equals(b[i].getName())) 
                    {
                        return false;
                    }
                }
                return true;
            }
        }
    }
}
